
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;
public class Bdd {
  public Hashtable<String,File> filelist;

  public Bdd() {

		this.filelist=new Hashtable<String,File>();
  }

  public synchronized File addFile(String key, long size, long piecesize) {
		File file=new File(key,size,piecesize);
		return filelist.put(file.key,file);
  }

  public synchronized boolean newListeningSeeder(String key, long size, long piecesize, String ip, int port, String filename) {
		if(filelist.containsKey(key)){
			filelist.get(key).addSeeder(ip,port,filename);
			return true;
		}
		else{
			addFile(key,size,piecesize);
			filelist.get(key).addSeeder(ip,port,filename);
			return true;
		}
  }

  public synchronized void removeListeningSeeder(String ip, int port) {
		String key=ip.concat(":").concat(String.valueOf(port));
		Enumeration<File> fileenum;
		fileenum=filelist.elements();

		while(fileenum.hasMoreElements()){
			fileenum.nextElement().seeders.remove(key);
		}
  }

  public synchronized boolean newListeningLeecher(String key, String ip, int port) {
		if(filelist.containsKey(key)){
			filelist.get(key).addLeecher(ip, port);
			return true;
		}
		return false;
  }

  public synchronized void removeListeningLeecher(String ip, int port) {
		String key=ip.concat(":").concat(String.valueOf(port));
		Enumeration<File> fileenum;
		fileenum=filelist.elements();

		while(fileenum.hasMoreElements()){
			fileenum.nextElement().leechers.remove(key);
		}
  }

  public void removeAll(String ip, int port) {
		removeListeningLeecher(ip, port);
		removeListeningSeeder(ip, port);
  }

  public Bdd findKeyFromFileName(String filename, String ipport) {
		Bdd result =new Bdd();
		Enumeration<File> fileenum;
		synchronized(this){
		fileenum=filelist.elements();
		}
		Enumeration<Seeder> seederenum;
		File tempfile;
		Seeder tempseeder;
		String tempipport;	
		Boolean gotit;
		while(fileenum.hasMoreElements()){
			tempfile=fileenum.nextElement();
			seederenum=tempfile.seeders.elements();
			gotit=false;
			while(seederenum.hasMoreElements() && !gotit){
				tempseeder=seederenum.nextElement();
				tempipport = tempseeder.ip.concat(":").concat(String.valueOf(tempseeder.port));	
				if(tempipport.equals(ipport)){
					gotit=true;
				}
			}
			seederenum=tempfile.seeders.elements();
			while(seederenum.hasMoreElements() && !gotit){
				tempseeder=seederenum.nextElement();
				//la condition :
				if(tempseeder.filename.equalsIgnoreCase(filename)){
					result.newListeningSeeder(tempfile.key,tempfile.size,tempfile.piecesize,tempseeder.ip,tempseeder.port,tempseeder.filename);	
					break;
				}
			}
		}
		return result;
  }

  public Bdd findKeyFromSizeOver(long size, String ipport) {
		Bdd result =new Bdd();
		Enumeration<File> fileenum;
		String tempipport;
		synchronized(this){
		fileenum=filelist.elements();
		}
		Enumeration<Seeder> seederenum;
		File tempfile;
		Seeder tempseeder;
		Boolean gotit;	 
		while(fileenum.hasMoreElements()){
			tempfile=fileenum.nextElement();
			seederenum=tempfile.seeders.elements();
			//la condition :
			if(tempfile.size>size){
				gotit=false;
				while(seederenum.hasMoreElements() && !gotit){
					tempseeder=seederenum.nextElement();
					tempipport = tempseeder.ip.concat(":").concat(String.valueOf(tempseeder.port));
					if(tempipport.equals(ipport)){
						gotit=true;
					}
				}
				seederenum=tempfile.seeders.elements();	
				while(seederenum.hasMoreElements() && !gotit){
					tempseeder=seederenum.nextElement();
					result.newListeningSeeder(tempfile.key,tempfile.size,tempfile.piecesize,tempseeder.ip,tempseeder.port,tempseeder.filename);	
					break;//On ne renvoit que le 1er qui marche
				}
			}
		}
		return result;
  }

  public Vector<Host> findIpFromKey(String key, String ipport) {
		Vector<Host> result =new Vector<Host>(3,2);
		File tempfile;
		String tempkey;
		synchronized(this){
			tempfile=filelist.get(key);
		}
		if(tempfile!=null){
			Enumeration<Seeder> seederenum = tempfile.seeders.elements();
			Seeder tempseeder;
			while(seederenum.hasMoreElements()){
				tempseeder=seederenum.nextElement();
				tempkey = tempseeder.ip.concat(":").concat(String.valueOf(tempseeder.port));
				if(!tempkey.equals(ipport))
					result.add(new Host(tempseeder.ip,tempseeder.port));
			}
			Enumeration<Leecher> leecherenum = tempfile.leechers.elements();
			Leecher templeecher;
			while(leecherenum.hasMoreElements()){
				templeecher=leecherenum.nextElement();
				tempkey = templeecher.ip.concat(":").concat(String.valueOf(templeecher.port));
				if(!tempkey.equals(ipport))
					result.add(new Host(templeecher.ip,templeecher.port));
			}
		}
		return result;
  }

  public synchronized void update(String[] new_seed, String[] new_leech, String ip, int port) {
		String key= ip.concat(":").concat(String.valueOf(port));
		String filename[]=new String[new_seed.length];
		//On supprime tous les fichiers que le paire leech et on ajoute ceux qu'il leech actuellement
		removeListeningLeecher(ip,port);
		Enumeration<Seeder> seederenum;
		for(int i=0;i<new_leech.length && !new_leech[i].equals("");i++){
			if(filelist.containsKey(new_leech[i])){
				filelist.get(new_leech[i]).addLeecher(ip,port);
			}
		}
		//Puis on sauve les fichiers qui sont déjà dans la bdd
		for(int i=0;i<new_seed.length && !new_seed[i].equals("");i++){
			if(filelist.containsKey(new_seed[i])){
				if(filelist.get(new_seed[i]).seeders.containsKey(key)){
					filename[i]=filelist.get(new_seed[i]).seeders.get(key).filename;
				}
				else{
					seederenum=filelist.get(new_seed[i]).seeders.elements();
					if(seederenum.hasMoreElements()){
					filename[i]=seederenum.nextElement().filename;	
					}
					else{
					filename[i]="_default";	
					}
				}
			}
			else{
			filename[i]=null;	
			}
		}
		removeListeningSeeder(ip,port);
		for(int i=0;i<new_seed.length && !new_seed[i].equals("");i++){
			if(filelist.containsKey(new_seed[i]) && filename[i]!=null){
				filelist.get(new_seed[i]).addSeeder(ip, port, filename[i]);
			}
		}
		return;
  }

  public void printKey() {
		Enumeration<File> fileenum;
		synchronized(this){
		fileenum=filelist.elements();
		}
		Enumeration<Seeder> seederenum;
		Enumeration<Leecher> leecherenum;
		File tempfile;
		Seeder tempseeder;
		Leecher templeecher;

		System.out.println("Bdd containt : ");
		while(fileenum.hasMoreElements()){
			tempfile=fileenum.nextElement();
			seederenum=tempfile.seeders.elements();
			leecherenum=tempfile.leechers.elements();
			System.out.println("File : " + tempfile.key + " "+ tempfile.size + " "+ tempfile.piecesize + " ");
			while(seederenum.hasMoreElements()){
				tempseeder=seederenum.nextElement();
				System.out.println("seeder : " + tempseeder.ip + " "+tempseeder.port+" "+tempseeder.filename);
			}
			while(leecherenum.hasMoreElements()){
				templeecher=leecherenum.nextElement();
				System.out.println("leecher : " + templeecher.ip + " "+templeecher.port);
			}
		}
  }

  public String printResearch() {
		Enumeration<File> fileenum;
		synchronized(this){
		fileenum=filelist.elements();
		}
		Enumeration<Seeder> seederenum;
		File tempfile;
		Seeder tempseeder;
		String returnvalue="";
		if(!fileenum.hasMoreElements())
			returnvalue="list []";
		else{
			returnvalue="list [";
			tempfile=fileenum.nextElement();
			seederenum=tempfile.seeders.elements();
			if(!seederenum.hasMoreElements())
				returnvalue="KO";
			else{
				tempseeder=seederenum.nextElement();
				returnvalue=returnvalue.concat(tempseeder.filename + " " + tempfile.size + " " + tempfile.size +" "+tempfile.key);
				while(seederenum.hasMoreElements()){
					tempseeder=seederenum.nextElement();
					returnvalue=returnvalue.concat(" " + tempseeder.filename + " " + tempfile.size + " " + tempfile.size +" "+tempfile.key);
				}
				while(fileenum.hasMoreElements()){
					tempfile=fileenum.nextElement();
					seederenum=tempfile.seeders.elements();
					if(!seederenum.hasMoreElements()){
						returnvalue="KO";
						break;
					}
					else{
						while(seederenum.hasMoreElements()){
							tempseeder=seederenum.nextElement();
							returnvalue=returnvalue.concat(" " + tempseeder.filename + " " + tempfile.size + " " + tempfile.size +" "+tempfile.key);
						}
					}
				}
				returnvalue=returnvalue.concat("]");
			}	
		}
		return returnvalue;
  }

  public String printIp(String key, String ipport) {
		Vector<Host> hostlist=findIpFromKey(key,ipport);
		String returnvalue="";

		if(hostlist.size()<1)
			returnvalue=("peers " + key + " []");
		else{
			returnvalue=("peers " + key +" [");
			returnvalue=returnvalue.concat(hostlist.get(0).ip + ":"+hostlist.get(0).port);
			for (int i=1; i<hostlist.size();i++){	
				returnvalue=returnvalue.concat(" "+hostlist.get(i).ip + ":"+hostlist.get(i).port);
			}
			returnvalue=returnvalue.concat("]");
		}

		return returnvalue;
  }

  public String answer(String message, TrackerThread thread) {
		String ip=thread._s.getInetAddress().getHostAddress();
		String[] temps=null;
		//On test si la requête est vide
		if(message.matches("look\\s+\\[\\s*\\]\\s*"))
			return "list []";
		//On test si c'est une requête qui existe/ qui est bien formée:
		//Si c'est une requête de recherche:
		if(message.matches("look\\s+\\[\\s*[(filename=\"[^\"\\s]+\"\\s*)(filesize>\"[0-9]+\"\\s*)]+\\]\\s*")){
			Bdd resultBdd=null;
			//On resort les informations pertinantes
			temps=message.split("(look\\s+\\[\\s*)|(\"\\s*)");
			//L'indice 0 est vide
			String ipport= thread._s.getInetAddress().getHostAddress().concat(":").concat(String.valueOf(thread._port));
			for(int i=1;i<temps.length-1;i+=2){
				if(temps[i].equals("filename=")){
					if(resultBdd==null)
						resultBdd=findKeyFromFileName(temps[i+1],ipport);
					else
						resultBdd=resultBdd.findKeyFromFileName(temps[i+1],ipport);
				}
				else{
					if(resultBdd==null)
						resultBdd=findKeyFromSizeOver(Integer.parseInt(temps[i+1]),ipport);
					else
						resultBdd=resultBdd.findKeyFromSizeOver(Integer.parseInt(temps[i+1]),ipport);
				}
			}
			return resultBdd.printResearch();
		}
		if(message.matches("getfile\\s+[0-9a-f]+\\s*")){
			//On resort les informations pertinantes
			temps=message.split("[\\s]+");
			String ipport= thread._s.getInetAddress().getHostAddress().concat(":").concat(String.valueOf(thread._port));
			return printIp(temps[1],ipport);
		}
		if(message.matches("announce\\s+listen\\s+[0-9]+(\\s+seed\\s+\\[\\s*([^\\s]+\\s+[0-9]+\\s+[0-9]+\\s+[0-9a-f]+\\s+)*([^\\s]+\\s+[0-9]+\\s+[0-9]+\\s+[0-9a-f]+)?\\])?(\\s+leech\\s+\\[\\s*([0-9a-f]+\\s*)*\\])?\\s*")){
			String[] seeder=null;
			String[] leecher=null;
			if(thread._port!=null);
			temps=message.split("(announce\\s+listen\\s+)|(\\s+seed\\s+\\[\\s*)|(\\s+leech\\s+\\[\\s*)");//(announce\\s+listen\\s+)(\\s+seed\\s+\\[)(\\[?\\s+leech\\s+\\[)
			//L'indice 0 est vide
			if(temps.length==2){
				return "ok";
			}
			int port=Integer.parseInt(temps[1]);
			thread._tracker.addId(thread._numClient,thread._s.getInetAddress().getHostAddress(),port);
			if(thread._tracker.isClient(thread._numClient)){
				System.out.println("Ip and port are already used");
				return "Ip and port are already used";
			}
			thread._port=new Integer(port);
			if(message.matches("announce\\s+listen\\s+[0-9]+\\s+seed.*")){
				//On resort les informations pertinantes
				temps[2]=temps[2].substring(0,temps[2].length()-1);
				seeder=temps[2].split("\\s+");
				for(int i=0;i<seeder.length-3;i+=4)
				newListeningSeeder(seeder[i+3],Long.parseLong(seeder[i+1]),Long.parseLong(seeder[i+2]),ip,port,seeder[i]);
			}
			if(message.matches("announce\\s+listen\\s+[0-9]+(\\s+seed\\s+\\[\\s*([^\\s]+\\s+[0-9]+\\s+[0-9]+\\s+[0-9a-zA-Z]+\\s+)*([^\\s]+\\s+[0-9]+\\s+[0-9]+\\s+[0-9a-zA-Z]+)?\\])?\\s+leech.*")){
				//On resort les informations pertinantes
				int j=(seeder==null)?2:3;
				temps[j]=temps[j].substring(0,temps[j].length()-1);
				leecher=temps[j].split("\\s+");
				for(int i=0;i<leecher.length;i++)
				newListeningLeecher(leecher[i],ip,port);			
			}
			return "ok";
		}
		if(message.matches("update(\\s+seed\\s+\\[\\s*([0-9a-zA-Z]+\\s*)*\\])?(\\s+leech\\s+\\[\\s*([0-9a-zA-Z]+\\s*)*\\])?\\s*")){
			String[] new_seed=null;
			String[] new_leech=null;
			temps=message.split("(update\\s+seed\\s+\\[\\s*)|(\\s+leech\\s+\\[\\s*)");
			//L'indice 0 est vide
			temps[1]=temps[1].substring(0,temps[1].length()-1);
			new_seed=temps[1].split("\\s+");
			temps[2]=temps[2].substring(0,temps[2].length()-1);
			new_leech=temps[2].split("\\s+");
			update(new_seed,new_leech,thread._s.getInetAddress().getHostAddress(),thread._port);
			return "ok";
		}
		//announce listen $Port seed [$Filename1 $Length1 $PieceSize1 $Key1 $Filename2 $Length2 $PieceSize2 $Key2 …] leech [$Key3 $Key4 …]
		return "KO";
  }

}
